package com.my_musc_service.common.aspect;

import com.my_musc_service.common.annotation.EncryptField;
import com.my_musc_service.common.annotation.EncryptMethod;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.jasypt.encryption.StringEncryptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Objects;

import static com.my_musc_service.common.enums.EncryptConstant.DECRYPT;
import static com.my_musc_service.common.enums.EncryptConstant.ENCRYPT;

/**
 * @author Zhu Dunfeng
 * @date 2021/8/19 12:06
 */
//@Slf4j
@Aspect
@Component
public class EncryptHandler {

    @Autowired
    private StringEncryptor stringEncryptor;

    /**切入点表达式*/
    @Pointcut("@annotation(com.my_musc_service.common.annotation.EncryptMethod)")
    public void pointCut() {}


    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) {

        Object result = null;

        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        EncryptMethod encryptMethod = method.getAnnotation(EncryptMethod.class);
        String type = encryptMethod.type();
        if (ENCRYPT.equals(type)) {
            /**
             * 加密
             */
            result = encrypt(joinPoint);
        } else if (DECRYPT.equals(type)) {
            /**
             * 解密
             */
            result = decrype(joinPoint);
        }
        return result;
    }

    private Object encrypt(ProceedingJoinPoint joinPoint) {
        Object result = null;
        try {
            Object[] objects = joinPoint.getArgs();
            if (objects.length != 0) {
                for (Object o : objects) {
                    if (o instanceof String) {
                        //传入的是对象的引用
                        result = encryptValue(o);
                    } else {
                        result = handler(o, ENCRYPT);
                    }

                    // TODO 其余类型自己看情况处理

                }
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return result;

    }

    private String encryptValue(Object realValue) {
        String value = null;
        value = stringEncryptor.encrypt(String.valueOf(realValue));
        return value;
    }


    public Object decrype(ProceedingJoinPoint joinPoint) {
        Object result = null;

        try {
            //调用目标方法
            Object obj = joinPoint.proceed();
            if (obj != null) {
                if (obj instanceof String) {
                    result = decrypeValue(obj);
                } else {
                    result = handler(obj, DECRYPT);
                }
                //TODO 其余类型自己看实际情况加
            }
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }

        return result;
    }

    private Object decrypeValue(Object realValue) {
        String value = String.valueOf(realValue);
        try {
            value = stringEncryptor.decrypt(value);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return value;
    }

    private Object handler(Object obj, String type) throws IllegalAccessException {
        if (Objects.isNull(obj)) {
            return null;
        }
        //获取当前类型的所有属性对象,包括私有
        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
            // 当前参数是否被EncryptField注解修饰
            boolean hasSecureField = field.isAnnotationPresent(EncryptField.class);
            if (hasSecureField) {
                //私有属性需要解除权限压制
                field.setAccessible(true);
                String realValue = (String) field.get(obj);
                String value;
                if (DECRYPT.equals(type)) {
                    value = stringEncryptor.decrypt(realValue);
                } else {
                    value = stringEncryptor.encrypt(realValue);
                }
                field.set(obj, value);
            }
        }
        return obj;
    }


}
